package com.piggy.logi.sys.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.common.collect.Lists;
import com.piggy.common.core.constant.SecurityConstants;
import com.piggy.common.core.constant.UserConstants;
import com.piggy.common.core.domain.R;
import com.piggy.common.core.enums.CacheKeyEnums;
import com.piggy.common.core.exception.CustomException;
import com.piggy.common.core.utils.FeignResultUtils;
import com.piggy.common.core.utils.PageUtils;
import com.piggy.common.core.web.base.BaseResponse;
import com.piggy.common.core.web.page.TableDataInfo;
import com.piggy.common.redis.cache.CacheUtils;
import com.piggy.common.redis.service.RedisService;
import com.piggy.common.satoken.utils.SecurityUtils;
import com.piggy.logi.common.datascope.annotation.LogiDataScope;
import com.piggy.logi.sys.api.domain.LogiSysDept;
import com.piggy.logi.sys.api.domain.LogiSysRole;
import com.piggy.logi.sys.api.domain.LogiSysUser;
import com.piggy.logi.sys.api.vo.LogiSysDeptExtVo;
import com.piggy.logi.sys.enums.DeptTypeEnum;
import com.piggy.logi.sys.enums.OrgDeptIdEnum;
import com.piggy.logi.sys.mapper.LogiSysDeptMapper;
import com.piggy.logi.sys.mapper.LogiSysRoleMapper;
import com.piggy.logi.sys.mapper.LogiSysUserMapper;
import com.piggy.logi.sys.service.ILogiSysDeptService;
import com.piggy.logi.sys.vo.LogiTreeSelect;
import com.piggy.sys.api.RemoteDistrictService;
import com.piggy.sys.api.vo.BaseAddressVo;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 部门管理 服务实现
 *
 * @author shark
 */
@Service
public class LogiSysDeptServiceImpl extends ServiceImpl<LogiSysDeptMapper, LogiSysDept> implements ILogiSysDeptService, BaseResponse {

    @Resource
    private LogiSysRoleMapper roleMapper;

    @Resource
    private LogiSysUserMapper userMapper;

    @Resource
    private ILogiSysDeptService deptService;

    @Resource
    private RemoteDistrictService districtService;

    @Resource
    RedisService redis;

    /**
     * 查询部门管理数据
     *
     * @param dept 部门信息
     * @return 部门信息集合
     */
    @Override
    @LogiDataScope(deptAlias = "d")
    public List<LogiSysDeptExtVo> selectDeptList(LogiSysDept dept) {
        List<LogiSysDeptExtVo> voList = baseMapper.selectDeptList(dept);
        voList.stream().peek(Any-> {
            R<BaseAddressVo> addressVoR = districtService.getDistrictVo(String.valueOf(Any.getDeptDistrict()), SecurityConstants.INNER);
            FeignResultUtils.throwIfFailed(addressVoR);
            BaseAddressVo addressVo = addressVoR.getData();
            if (ObjectUtil.isNotEmpty(addressVo)) {
                Any.setDeptDistrictName(addressVo.getProvName()+addressVo.getCityName()+addressVo.getDistName());
            }
        }).collect(Collectors.toList());
        return voList;
    }

    @Override
    public TableDataInfo<LogiSysDeptExtVo> selectRootDeptList(LogiSysDept dept) {
        dept.setParentId(0L);
        PageInfo<LogiSysDeptExtVo> result = startPage(dept).doSelectPageInfo(() -> {
            List<LogiSysDeptExtVo> voList = baseMapper.selectDeptList(dept);
        });

        result.getList().stream().peek(Any-> {
            R<BaseAddressVo> addressVoR = districtService.getDistrictVo(String.valueOf(Any.getDeptDistrict()), SecurityConstants.INNER);
            FeignResultUtils.throwIfFailed(addressVoR);
            BaseAddressVo addressVo = addressVoR.getData();
            if (ObjectUtil.isNotEmpty(addressVo)) {
                Any.setDeptDistrictName(addressVo.getProvName()+addressVo.getCityName()+addressVo.getDistName());
            }
        }).collect(Collectors.toList());
        return PageUtils.buildDataInfo(result);
    }

    /**
     * 查询部门管理数据
     *
     * @param dept 部门信息
     * @return 部门信息集合
     */
    @Override
    @LogiDataScope(deptAlias = "d")
    public List<LogiSysDeptExtVo> selectBranchDeptList(LogiSysDept dept) {
        return baseMapper.selectDeptList(dept);
    }

    @Override
    @LogiDataScope(deptAlias = "d")
    public List<LogiSysDeptExtVo> selectGeoDeptList(BigDecimal lng, BigDecimal lat) {
        LogiSysDept dept = new LogiSysDept().setDeptTypes(Lists.newArrayList(DeptTypeEnum.BRANCH.getType(),DeptTypeEnum.CARGO.getType(),DeptTypeEnum.ALLOC_CENTER.getType()));
        List<LogiSysDeptExtVo> voList = baseMapper.selectDeptList(dept);
        CacheUtils.remove(CacheKeyEnums.LogiGeoBranch.getKey(SecurityUtils.getDeptId()));
        voList.forEach(t-> {
            boolean ignore = ObjectUtil.hasEmpty(t.getDeptLng(), t.getDeptLat()) ||
                    NumberUtil.isLessOrEqual(t.getDeptLng(), BigDecimal.ZERO) ||
                    NumberUtil.isLessOrEqual(t.getDeptLng(), BigDecimal.ZERO);
            if (ignore) {
                return;
            }
            redis.geoAdd(CacheKeyEnums.LogiGeoBranch.getKey(SecurityUtils.getDeptId()), t.getDeptLng().doubleValue(), t.getDeptLat().doubleValue(), String.valueOf(t.getDeptId()));
        });
        List<RedisService.RadiusMem> radius = redis.geoRadius(CacheKeyEnums.LogiGeoBranch.getKey(SecurityUtils.getDeptId()), lng.doubleValue(), lat.doubleValue(), 5, RedisGeoCommands.DistanceUnit.KILOMETERS);
        if (CollUtil.isEmpty(radius)) {
            return new ArrayList<>();
        }
        LinkedHashMap<Long,LogiSysDeptExtVo> deptMap = voList.stream().collect(LinkedHashMap::new, (m, v) -> m.put(v.getDeptId(), v), LinkedHashMap::putAll);
        return radius.stream().map(Any-> {
            LogiSysDeptExtVo vo = deptMap.get(Long.valueOf(Any.getMember()));
            vo.setDistince(BigDecimal.valueOf(Any.getDistance().getValue()));
            return vo;
        }).collect(Collectors.toList());
    }

    @Override
    @LogiDataScope(deptAlias = "d")
    public List<LogiSysDeptExtVo> selectGeoLineDeptList(List<Long> deptIds, BigDecimal lng, BigDecimal lat) {
        LogiSysDept dept = new LogiSysDept().setDeptTypes(Lists.newArrayList(DeptTypeEnum.BRANCH.getType(),DeptTypeEnum.CARGO.getType(),DeptTypeEnum.ALLOC_CENTER.getType()));
        dept.setDeptIds(deptIds);
        List<LogiSysDeptExtVo> voList = baseMapper.selectDeptList(dept);
        CacheUtils.remove(CacheKeyEnums.LogiGeoLineBranch.getKey(SecurityUtils.getDeptId()));
        voList.forEach(t-> {
            boolean ignore = ObjectUtil.hasEmpty(t.getDeptLng(), t.getDeptLat()) ||
                    NumberUtil.isLessOrEqual(t.getDeptLng(), BigDecimal.ZERO) ||
                    NumberUtil.isLessOrEqual(t.getDeptLng(), BigDecimal.ZERO);
            if (ignore) {
                return;
            }
            redis.geoAdd(CacheKeyEnums.LogiGeoLineBranch.getKey(SecurityUtils.getDeptId()), t.getDeptLng().doubleValue(), t.getDeptLat().doubleValue(), String.valueOf(t.getDeptId()));
        });
        List<RedisService.RadiusMem> radius = redis.geoRadius(CacheKeyEnums.LogiGeoLineBranch.getKey(SecurityUtils.getDeptId()), lng.doubleValue(), lat.doubleValue(), 5, RedisGeoCommands.DistanceUnit.KILOMETERS);
        if (CollUtil.isEmpty(radius)) {
            return new ArrayList<>();
        }
        LinkedHashMap<Long,LogiSysDeptExtVo> deptMap = voList.stream().collect(LinkedHashMap::new, (m, v) -> m.put(v.getDeptId(), v), LinkedHashMap::putAll);
        return radius.stream().map(Any-> {
            LogiSysDeptExtVo vo = deptMap.get(Long.valueOf(Any.getMember()));
            vo.setDistince(BigDecimal.valueOf(Any.getDistance().getValue()));
            return vo;
        }).collect(Collectors.toList());
    }

    /**
     * 构建前端所需要树结构
     *
     * @param depts 部门列表
     * @return 树结构列表
     */
    @Override
    public List<LogiSysDeptExtVo> buildDeptTree(List<LogiSysDeptExtVo> depts) {
        List<LogiSysDeptExtVo> returnList = new ArrayList<LogiSysDeptExtVo>();
        List<Long> tempList = new ArrayList<Long>();
        for (LogiSysDeptExtVo dept : depts) {
            tempList.add(dept.getDeptId());
        }
        for (Iterator<LogiSysDeptExtVo> iterator = depts.iterator(); iterator.hasNext(); ) {
            LogiSysDeptExtVo dept = (LogiSysDeptExtVo) iterator.next();
            // 如果是顶级节点, 遍历该父节点的所有子节点
            if (!tempList.contains(dept.getParentId())) {
                recursionFn(depts, dept);
                returnList.add(dept);
            }
        }
        if (returnList.isEmpty()) {
            returnList = depts;
        }
        return returnList;
    }

    /**
     * 构建前端所需要下拉树结构
     *
     * @param depts 部门列表
     * @return 下拉树结构列表
     */
    @Override
    public List<LogiTreeSelect> buildDeptTreeSelect(List<LogiSysDeptExtVo> depts) {
        List<LogiSysDeptExtVo> deptTrees = buildDeptTree(depts);
        return deptTrees.stream().map(LogiTreeSelect::new).collect(Collectors.toList());
    }

    /**
     * 根据角色ID查询部门树信息
     *
     * @param roleId 角色ID
     * @return 选中部门列表
     */
    @Override
    public List<Integer> selectDeptListByRoleId(Long roleId) {
        LogiSysRole role = roleMapper.selectById(roleId);
        return baseMapper.selectDeptListByRoleId(roleId, role.isDeptCheckStrictly());
    }

    /**
     * 根据部门ID查询信息
     *
     * @param deptId 部门ID
     * @return 部门信息
     */
    @Override
    public LogiSysDeptExtVo selectDeptById(Long deptId) {
        LogiSysDeptExtVo extVo = baseMapper.getDeptByDeptId(deptId);
        if (ObjectUtil.isEmpty(extVo)) {
            return extVo;
        }
        R<BaseAddressVo> addressVoR = districtService.getDistrictVo(ObjectUtil.defaultIfNull(extVo.getDeptDistrict(),0L).toString(), SecurityConstants.INNER);
        FeignResultUtils.throwIfFailed(addressVoR);
        BaseAddressVo addressVo = addressVoR.getData();
        if (ObjectUtil.isNotEmpty(addressVo)) {
            extVo.setDeptDistrictName(addressVo.getProvName()+addressVo.getCityName()+addressVo.getDistName());
        }
        return extVo;
    }

    /**
     * 根据ID查询所有子部门（正常状态）
     *
     * @param deptId 部门ID
     * @return 子部门数
     */
    @Override
    public int selectNormalChildrenDeptById(Long deptId) {
        Long count = count(new LambdaQueryWrapper<LogiSysDept>()
                .eq(LogiSysDept::getStatus, 0)
                .apply("find_in_set({0}, ancestors)", deptId));
        return count.intValue();
    }

    /**
     * 获取用户所属组织的部门ID(如车商部门ID、资方部门ID、中外运部门ID、平台部门ID)
     */
    @Override
    public Long getOrgDeptId(Long deptId) {

        LogiSysDept dept = this.getById(deptId);
        String[] deptArr = dept.getAncestors().split(",");
        Long orgDeptId = null;//车商部门id  如：124 招商汽贸、125一汽汽贸 ；资方部门id  如：118 中商国能、119 建设银行； 中外运部门id  如：112 中外运

        if (OrgDeptIdEnum.SYSADMIN.getCode().equals(deptId.intValue())) {
            //100 系统管理员
            orgDeptId = deptId;
        } else {
            if (deptArr.length == 2) {
                //直接在第2层，取其部门id
                orgDeptId = dept.getDeptId();
            } else if (deptArr.length == 3 || deptArr.length == 4 || deptArr.length == 5 || deptArr.length == 6 || deptArr.length == 7 || deptArr.length == 8 || deptArr.length == 9) {
                //其部门在第3层、第4层、第5层、第6层、第7层、第8层、第9层，取其Ancestors数组下标为2
                orgDeptId = Long.valueOf(deptArr[2]);
            } else {
            }
        }
        return orgDeptId;
    }

    /**
     * 是否存在子节点
     *
     * @param deptId 部门ID
     * @return 结果
     */
    @Override
    public boolean hasChildByDeptId(Long deptId) {
        Long result = count(new LambdaQueryWrapper<LogiSysDept>()
                .eq(LogiSysDept::getParentId, deptId)
                .last("limit 1"));
        return result > 0L ? true : false;
    }

    /**
     * 查询部门是否存在用户
     *
     * @param deptId 部门ID
     * @return 结果 true 存在 false 不存在
     */
    @Override
    public boolean checkDeptExistUser(Long deptId) {
        Long result = userMapper.selectCount(new LambdaQueryWrapper<LogiSysUser>()
                .eq(LogiSysUser::getDeptId, deptId));
        return result > 0 ? true : false;
    }

    /**
     * 校验部门名称是否唯一
     *
     * @param dept 部门信息
     * @return 结果
     */
    @Override
    public String checkDeptNameUnique(LogiSysDept dept) {
        Long deptId = Validator.isNull(dept.getDeptId()) ? -1L : dept.getDeptId();
        /*if (StrUtil.isNotBlank(dept.getDeptName())) {
            LogiSysDept info = getOne(new LambdaQueryWrapper<LogiSysDept>()
                    .eq(LogiSysDept::getDeptName, dept.getDeptName())
                    .eq(LogiSysDept::getParentId, dept.getParentId())
                    .last("limit 1"));
            if (Validator.isNotNull(info) && info.getDeptId().longValue() != deptId.longValue()) {
                return UserConstants.NOT_UNIQUE;
            }
        } else if (StrUtil.isNotBlank(dept.getDeptCode())) {
            LogiSysDept info = getOne(new LambdaQueryWrapper<LogiSysDept>()
                    .eq(LogiSysDept::getDeptCode, dept.getDeptCode())
                    .eq(LogiSysDept::getParentId, dept.getParentId())
                    .last("limit 1"));
            if (Validator.isNotNull(info) && info.getDeptId().longValue() != deptId.longValue()) {
                return UserConstants.NOT_UNIQUE;
            }
        }*/
        LogiSysDeptExtVo info = baseMapper.getDeptByDept(new LogiSysDept().
                setDeptName(dept.getDeptName()).
                setDeptCode(dept.getDeptCode()).
                setMainDeptId(dept.getMainDeptId()).
                setParentId(dept.getParentId()));
        if (ObjectUtil.isNotEmpty(info) &&
                (!ObjectUtil.equal(info.getDeptId(), deptId) &&
                        !StrUtil.equals(info.getDeptName(), dept.getDeptName()) &&
                        !StrUtil.equals(info.getDeptCode(), dept.getDeptCode()))) {
            return UserConstants.NOT_UNIQUE;
        }

        return UserConstants.UNIQUE;
    }

    /**
     * 新增保存部门信息
     *
     * @param dept 部门信息
     * @return 结果
     */
    @Override
    public int insertDept(LogiSysDept dept) {

        if (dept.getParentId() == 0L) {
            dept.setAncestors(String.valueOf(dept.getParentId()));
            return baseMapper.insert(dept);
        }

        LogiSysDept info = baseMapper.selectById(dept.getParentId());
        // 如果父节点不为正常状态,则不允许新增子节点
        if (!UserConstants.DEPT_NORMAL.equals(info.getStatus())) {
            throw new CustomException("部门停用，不允许新增");
        }

        dept.setAncestors(info.getAncestors() + "," + dept.getParentId()).setMainDeptId(info.getMainDeptId());
        return baseMapper.insert(dept);

    }

    /**
     * 修改保存部门信息
     *
     * @param dept 部门信息
     * @return 结果
     */
    @Override
    public int updateDept(LogiSysDept dept) {
        LogiSysDept newParentDept = baseMapper.selectById(dept.getParentId());
        LogiSysDept oldDept = baseMapper.selectById(dept.getDeptId());
        if (Validator.isNotNull(newParentDept) && Validator.isNotNull(oldDept)) {
            String newAncestors = newParentDept.getAncestors() + "," + newParentDept.getDeptId();
            String oldAncestors = oldDept.getAncestors();
            dept.setAncestors(newAncestors);
            updateDeptChildren(dept.getDeptId(), newAncestors, oldAncestors);
        }
        int result = baseMapper.updateById(dept);
        if (UserConstants.DEPT_NORMAL.equals(dept.getStatus())) {
            // 如果该部门是启用状态，则启用该部门的所有上级部门
            updateParentDeptStatus(dept);
        }
        return result;
    }

    /**
     * 修改该部门的父级部门状态
     *
     * @param dept 当前部门
     */
    private void updateParentDeptStatus(LogiSysDept dept) {
        String updateBy = dept.getUpdateBy();
        dept = getById(dept.getDeptId());
        dept.setUpdateBy(updateBy);
        update(null, new LambdaUpdateWrapper<LogiSysDept>()
                .set(StrUtil.isNotBlank(dept.getStatus()),
                        LogiSysDept::getStatus, dept.getStatus())
                .set(StrUtil.isNotBlank(dept.getUpdateBy()),
                        LogiSysDept::getUpdateBy, dept.getUpdateBy())
                .in(LogiSysDept::getDeptId, Arrays.asList(dept.getAncestors().split(","))));
    }

    /**
     * 修改子元素关系
     *
     * @param deptId       被修改的部门ID
     * @param newAncestors 新的父ID集合
     * @param oldAncestors 旧的父ID集合
     */
    public void updateDeptChildren(Long deptId, String newAncestors, String oldAncestors) {
        List<LogiSysDept> children = list(new LambdaQueryWrapper<LogiSysDept>()
                .apply("find_in_set({0},ancestors)", deptId));
        for (LogiSysDept child : children) {
            child.setAncestors(child.getAncestors().replaceFirst(oldAncestors, newAncestors));
        }
        if (children.size() > 0) {
            baseMapper.updateDeptChildren(children);
        }
    }

    /**
     * 删除部门管理信息
     *
     * @param deptId 部门ID
     * @return 结果
     */
    @Override
    public int deleteDeptById(Long deptId) {
        return baseMapper.deleteById(deptId);
    }

    @Override
    public List<LogiSysDeptExtVo> queryDeptAll() {
        List<LogiSysDeptExtVo> list = baseMapper.selectDeptList(new LogiSysDept());
        list.stream().peek(Any-> {
            R<BaseAddressVo> addressVoR = districtService.getDistrictVo(Any.getDeptDistrict()+"", SecurityConstants.INNER);
            FeignResultUtils.throwIfFailed(addressVoR);
            BaseAddressVo addressVo = addressVoR.getData();
            if (ObjectUtil.isNotEmpty(addressVo)) {
                Any.setDeptDistrictName(addressVo.getProvName()+addressVo.getCityName()+addressVo.getDistName());
            }
        }).collect(Collectors.toList());
        return list;
    }

    @Override
    public List<LogiTreeSelect> selectDeptTreeList(LogiSysDept dept) {
        List<LogiSysDeptExtVo> depts = deptService.selectDeptList(dept);
        return buildDeptTreeSelect(depts);
    }

    /**
     * 递归列表
     */
    private void recursionFn(List<LogiSysDeptExtVo> list, LogiSysDeptExtVo t) {
        // 得到子节点列表
        List<LogiSysDeptExtVo> childList = getChildList(list, t);
        t.setChildren(childList);
        for (LogiSysDeptExtVo tChild : childList) {
            if (hasChild(list, tChild)) {
                recursionFn(list, tChild);
            }
        }
    }

    /**
     * 得到子节点列表
     */
    private List<LogiSysDeptExtVo> getChildList(List<LogiSysDeptExtVo> list, LogiSysDeptExtVo t) {
        List<LogiSysDeptExtVo> tlist = new ArrayList<>();
        Iterator<LogiSysDeptExtVo> it = list.iterator();
        while (it.hasNext()) {
            LogiSysDeptExtVo n = it.next();
            if (Validator.isNotNull(n.getParentId()) && n.getParentId().longValue() == t.getDeptId().longValue()) {
                tlist.add(n);
            }
        }
        return tlist;
    }

    /**
     * 判断是否有子节点
     */
    private boolean hasChild(List<LogiSysDeptExtVo> list, LogiSysDeptExtVo t) {
        return getChildList(list, t).size() > 0 ? true : false;
    }

    @Override
    public List<LogiSysDeptExtVo> selectDeptByIds(List<Long> deptIds) {
        return baseMapper.getDeptByDeptIds(deptIds);
    }

}
